package com.garrigou.spaceship.screens.scene2d;

import com.badlogic.gdx.Gdx;
import com.badlogic.gdx.Input;
import com.badlogic.gdx.Input.Peripheral;
import com.badlogic.gdx.graphics.GL20;
import com.badlogic.gdx.graphics.Texture;
import com.badlogic.gdx.graphics.g2d.SpriteBatch;
import com.badlogic.gdx.graphics.g2d.TextureRegion;
import com.badlogic.gdx.math.Vector2;
import com.badlogic.gdx.math.Vector3;
import com.badlogic.gdx.scenes.scene2d.Actor;
import com.garrigou.spaceship.utils.VectorUtils;

public class OrthoCamController extends Actor
{
    private final ParallaxCamera  camera;
    private final Vector3         curr  = new Vector3();
    private final Vector3         last  = new Vector3(-1, -1, -1);
    private final Vector3         delta = new Vector3();
    private final SpriteBatch     parentBatch;
    private final TextureRegion[] layers;
    private Ship2D                ship;

    public OrthoCamController(ParallaxCamera camera, SpriteBatch batch)
    {
        this.camera = camera;

        Texture texture = new Texture(Gdx.files.internal("data/layers.png"));
        this.layers = new TextureRegion[3];
        this.layers[0] = new TextureRegion(texture, 0, 0, 542, 363);
        this.layers[1] = new TextureRegion(texture, 0, 363, 1024, 149);
        this.layers[2] = new TextureRegion(texture, 547, 0, 224, 51);

        // set some basic attributes
        this.parentBatch = batch;
        this.position = new Vector2();
        this.velocity = new Vector2();
        this.acceleration = new Vector2();
    }

    public void setShip(Ship2D ship)
    {
        this.ship = ship;
    }

    @Override
    public void act(
            float delta)
    {
        super.act(delta);

        calculCameraPosition(delta);

        if (this.ship == null)
            moveCamera(0, 0, -1);
        else
            moveCamera((int) /*this.ship.getShipPosition()*/position.x, (int) /*this.ship.getShipPosition()*/position.y, -1);

        Gdx.gl.glClearColor(242 / 255.0f, 210 / 255.0f, 111 / 255.0f, 1);
        Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT);

        // keep camera in foreground layer bounds
// boolean updateCamera = false;
// if (camera.position.x < -1024 + camera.viewportWidth / 2)
// {
// camera.position.x = -1024 + (int) (camera.viewportWidth / 2);
// updateCamera = true;
// }
//
// if (camera.position.x > 1024 - camera.viewportWidth / 2)
// {
// camera.position.x = 1024 - (int) (camera.viewportWidth / 2);
// updateCamera = true;
// }

// if (camera.position.y < 0)
// {
// camera.position.y = 0;
// updateCamera = true;
// }
        // arbitrary height of scene
// if (camera.position.y > 400 - camera.viewportHeight / 2)
// {
// camera.position.y = 400 - (int) (camera.viewportHeight / 2);
// updateCamera = true;
// }

        // background layer, no parallax, centered around origin
        parentBatch.setProjectionMatrix(camera.calculateParallaxMatrix(0, 0));
        parentBatch.disableBlending();
        parentBatch.begin();
        parentBatch.draw(layers[0], -(int) (layers[0].getRegionWidth() / 2), -(int) (layers[0].getRegionHeight() / 2));
        parentBatch.end();
        parentBatch.enableBlending();

        // midground layer, 0.5 parallax (move at half speed on x, full speed on y)
        // layer is 1024x320
        parentBatch.setProjectionMatrix(camera.calculateParallaxMatrix(0.5f, 1));
        parentBatch.begin();
        parentBatch.draw(layers[1], -512, -160);
        parentBatch.end();

        // foreground layer, 1.0 parallax (move at full speed)
        // layer is 2048x320
        parentBatch.setProjectionMatrix(camera.calculateParallaxMatrix(1f, 1));
        parentBatch.begin();
        for (int i = 0; i < 9; i++)
        {
            parentBatch.draw(layers[2], i * layers[2].getRegionWidth() - 1024, -160);
        }
        parentBatch.end();
    }

    /**
     * The ship's maximum speed; given in pixels per second.
     */
    private static final float MAX_SPEED        = 240;

    /**
     * The ship's maximum acceleration; given in pixels per second².
     */
    private static final float MAX_ACCELERATION = 10;

    /**
     * The ship's maximum deceleration; given in pixels per second².
     * <p>
     * This is the force that makes the ship to stop flying.
     */
    private static final float MAX_DECELERATION = MAX_ACCELERATION / 2;

    /**
     * The ship's position.
     */
    private final Vector2      position;

    /**
     * The ship's velocity.
     */
    private final Vector2      velocity;

    /**
     * The ship's acceleration.
     */
    private final Vector2      acceleration;

    private void calculCameraPosition(float delta)
    {
        if (Gdx.input.isPeripheralAvailable(Peripheral.Accelerometer))
        {

            // set the acceleration base on the accelerometer input; notice the
            // inverted axis because the game is displayed in landscape mode
            acceleration.set(Gdx.input.getAccelerometerY(), Gdx.input.getAccelerometerX());

            // set the acceleration bounds
            VectorUtils.adjustByRange(acceleration, -2, 2);

            // set the input deadzone
            if (!VectorUtils.adjustDeadzone(acceleration, 1f, 0f))
            {
                // we're out of the deadzone, so let's adjust the acceleration
                // (2 is 100% of the max acceleration)
                acceleration.x = (acceleration.x / 2 * MAX_ACCELERATION);
                acceleration.y = (-acceleration.y / 2 * MAX_ACCELERATION);
            }

        }
        else
        {
            // when the keys aren't pressed the acceleration will be zero, so
            // the ship's velocity won't be affected by it
            acceleration.x = (Gdx.input.isKeyPressed(Input.Keys.LEFT) ? -MAX_ACCELERATION
                    : (Gdx.input.isKeyPressed(Input.Keys.RIGHT) ? MAX_ACCELERATION : 0));
            acceleration.y = (Gdx.input.isKeyPressed(Input.Keys.UP) ? MAX_ACCELERATION
                    : (Gdx.input.isKeyPressed(Input.Keys.DOWN) ? -MAX_ACCELERATION : 0));
        }

        // if there is no acceleration and the ship is moving, let's calculate
        // an appropriate deceleration
        if (acceleration.len() == 0f && velocity.len() > 0f)
        {

            // horizontal deceleration
            if (velocity.x > 0)
            {
                acceleration.x = -MAX_DECELERATION;
                if (velocity.x - acceleration.x < 0)
                {
                    acceleration.x = -(acceleration.x - velocity.x);
                }
            }
            else if (velocity.x < 0)
            {
                acceleration.x = MAX_DECELERATION;
                if (velocity.x + acceleration.x > 0)
                {
                    acceleration.x = (acceleration.x - velocity.x);
                }
            }

            // vertical deceleration
            if (velocity.y > 0)
            {
                acceleration.y = -MAX_DECELERATION;
                if (velocity.y - acceleration.y < 0)
                {
                    acceleration.y = -(acceleration.y - velocity.y);
                }
            }
            else if (velocity.y < 0)
            {
                acceleration.y = MAX_DECELERATION;
                if (velocity.y + acceleration.y > 0)
                {
                    acceleration.y = (acceleration.y - velocity.y);
                }
            }

        }

        // modify and check the ship's velocity
        velocity.add(acceleration);
        VectorUtils.adjustByRange(velocity, -MAX_SPEED, MAX_SPEED);

        // modify and check the ship's position, applying the delta parameter
        position.add(velocity.x * delta, velocity.y * delta);
        
        if( VectorUtils.adjustByRangeY( position, -20, ( getStage().getHeight() - getHeight() ) *2 ) )
            velocity.y = 0;
    }

    public void moveCamera(int x, int y, int pointer)
    {
        camera.unproject(curr.set(x, y, 0));
        if (!(last.x == -1 && last.y == -1 && last.z == -1))
        {
            camera.unproject(delta.set(last.x, last.y, 0));
            delta.sub(curr);
            camera.position.add(delta.x, delta.y, 0);
        }
        
        last.set(x, y, 0);
    }
}
